/**
 *
 * =============================================================================
 *
 * =============================================================================
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License, version 3.0, as published by the
 * Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * As a special exception, AlliedModders LLC gives you permission to link the
 * code of this program (as well as its derivative 1works) to "Half-Life 2," the
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
 * by the Valve Corporation.  You must obey the GNU General Public License in
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
 * this exception to all derivative works.  AlliedModders LLC defines further
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
 * or <http://www.sourcemod.net/license.php>.
 *
 *
 */

#include <sourcemod>
#include <sdktools>
#include <sdkhooks>
#include <mapchooser>

#define SOUND_UP	"ambient/energy/weld1.wav"
#define SOUND_TELE	"ambient/machines/teleport1.wav"
#define PARTICLE	"embers_small_01"
#define SPRITETRAIL	"sprites/bluelaser1.vmt"

#define VERSION "3.1"

public Plugin:myinfo =
{
	name = "A Plugin for Synergy",
	author = "sh1thappens aka DJ (Tempo-)ReDRoX",
	description = "A Plugin for Synergy",
	version = VERSION,
	url = "http://bttrservers.net/"
}

//-----------------------//
// Here we go some cvars //
//-----------------------//
new Handle:g_Timelimit = INVALID_HANDLE
new Handle:g_SetHealth = INVALID_HANDLE
new Handle:m_iGameConf
new bool:Synergy = false
new bool:CheckMapEnd = false

// Set Debug to true if you want to know more about plugin messages
new bool:Debug = false // true

//-----------------------------//
// Add all the necessary files //
//-----------------------------//
#include "synplugin\info.sp"
#include "synplugin\spec.sp"
#include "synplugin\name.sp"
#include "synplugin\dissolve.sp"
#include "synplugin\falldamage.sp"
#include "synplugin\goto.sp"
#include "synplugin\teleport.sp"
#include "synplugin\save.sp"
#include "synplugin\addbot.sp"
#include "synplugin\items.sp"
#include "synplugin\respawn.sp"
#include "synplugin\staticspawn.sp"
#include "synplugin\mapend.sp"
#include "synplugin\infinitesprint.sp"
#include "synplugin\chatcommands.sp"
#include "synplugin\weapon.sp"
#include "synplugin\vehicle.sp"
#include "synplugin\extend.sp"
#include "synplugin\customfiles.sp"
#include "synplugin\custommapfix.sp"
#include "synplugin\cvars.sp"

public OnPluginStart()
{
	LoadTranslations("synplugin.phrases")
	GetCvars() // all cvars found in "synplugin\cvars.sp"
	GameDetect() // Which game do we run?

	AutoExecConfig(true, "syn_plugin") // if not exist make automatically a config file in cfg/sourcemod

	HookEventEx("player_death", OnPlayerDeath, EventHookMode_Post)
	HookEventEx("player_spawn", OnPlayerSpawn, EventHookMode_Post)
	HookEventEx("intermission_time", OnEndMap, EventHookMode_Pre)
	HookEventEx("entity_killed", OnEntityKilled, EventHookMode_Post)

	RegAdminCmd("sm_syn_save", Save, ADMFLAG_GENERIC, "Saves the game")
	RegAdminCmd("sm_syn_extend_time", ExtendTime, ADMFLAG_GENERIC)
	AddCommandListener(Say_Command, "say")
	AddCommandListener(Say_Command, "say_team")
	RegConsoleCmd("sm_goto", Do_Goto)
	RegConsoleCmd("sm_teleme", TeleClient)
	RegConsoleCmd("sm_saveme", SaveClientLocation)
	RegConsoleCmd("sm_save", SaveClientLocation)
	RegConsoleCmd("sm_spec", Do_Spec)
	RegConsoleCmd("sm_spectator", Do_Spec)
	
	g_Timelimit = FindConVar("mp_timelimit")

	m_iGameConf = LoadGameConfigFile("bttr.synergy.games")

	StartPrepSDKCall(SDKCall_Player)
	PrepSDKCall_SetFromConf(m_iGameConf, SDKConf_Virtual, "PreSpawn")
	m_PreSpawn = EndPrepSDKCall()

	StartPrepSDKCall(SDKCall_Player)
	PrepSDKCall_SetFromConf(m_iGameConf, SDKConf_Virtual, "Spawn")
	m_Spawn = EndPrepSDKCall()

	StartPrepSDKCall(SDKCall_Static)
	PrepSDKCall_SetFromConf(m_iGameConf, SDKConf_Signature, "DoSave")
	m_DoSave = EndPrepSDKCall()

	if((m_DoSave == INVALID_HANDLE) || (m_PreSpawn == INVALID_HANDLE) || (m_Spawn == INVALID_HANDLE))
	{
		SetFailState("[SYN] Signature Failed. Please contact the author.")
		// stops the plugin if any signature failed, otherwise it would
		// probably crash the server
	}
}

//-----------------------//
// Which game do we run? //
//-----------------------//
GameDetect()
{
	new String:GetMod[16]
	GetGameFolderName(GetMod, sizeof(GetMod))
	if (strcmp(GetMod, "synergy", false) == 0)
	{
		PrintToServer("[SYN] Game detected Mod: Synergy")
		Synergy = true
	}
	else
	{
		PrintToServer("[SYN] Game detected Mod: Other. Other games are not supported!")
	}
}

//------------------------------------------------------//
// This FindEntity function is better than the original //
//------------------------------------------------------//
stock FindEntityByClassname2(startEnt, const String:classname[])
{
	/* If startEnt isn't valid shifting it back to the nearest valid one */
	while (startEnt > -1 && !IsValidEntity(startEnt)) startEnt--

	return FindEntityByClassname(startEnt, classname)
}

//----------------//
// OnEntityKilled //
//----------------//
public Action:OnEntityKilled(Handle:event, const String:name[], bool:dontBroadcast)
{
	new client = GetEventInt(event, "entindex_attacker")
	if (client <= 0){
		if (Debug)
			PrintToChat(client, "[SYN] This should never happen!")
		return Plugin_Handled
	}
	
	new entity = GetEventInt(event, "entindex_killed")
	if (entity <= 0){
		if (Debug)
			PrintToChat(client, "[SYN] This should never happen!")
		return Plugin_Handled
	}
	new String:classname[32]
	GetEdictClassname(entity, classname, sizeof(classname))
//	decl String:org_name[MAX_NAME_LENGTH]
//	GetClientName(client, org_name, sizeof(org_name))
//	PrintToChat(client, "[SYN] Yeha! %s killed %s", org_name, classname)
	
	if (StrContains(classname, "npc_", false) != -1)
	{
		if (GetConVarInt(g_DissolvePlayer) == 1){
			DO_DissolvePlayer(INVALID_HANDLE, entity)
		}
	}
	return Plugin_Handled
}
//---------------//
// OnPlayerSpawn //
//---------------//
public Action:OnPlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast)
{
	// Check if the Player/Client really exists 
	new client = GetClientOfUserId(GetEventInt(event, "userid"))
	if (client <= 0){
		if (Debug)
			PrintToChat(client, "[SYN] This should never happen!")
		return Plugin_Handled
	}

	// Now we setting Health if enabled 
	if(GetConVarInt(g_SetHealth) >= 1){
		CreateTimer(0.1, DO_SetHealth, client)
	}
	
	// If its a Fake Player/Client (bots usually) we give him GodMode
	if (IsFakeClient(client)){
		CreateTimer(0.1, BotGod, client)
	}
	
	// If autosave enabled we start the timer right here, until someone dies
	if (GetConVarInt(g_Save) == 1 && GetConVarInt(g_SaveDelay) > 10 && (m_timer == INVALID_HANDLE)){
		new Float:delay = GetConVarFloat (g_SaveDelay)
		m_timer = CreateTimer(delay, DO_Save, _, TIMER_REPEAT)
		if (Debug)
			PrintToChat(client, "[SYN] Save timer started")
	}
	return Plugin_Handled
}

//-------------------------------------------------------------------//
// Here we setting the Health of a player when he spawns in the game //
//-------------------------------------------------------------------//
public Action:DO_SetHealth(Handle:timer, any:client)
{
	if (PlayerSpawned(client)){
		// Getting value from cvar
		new m_Health = GetConVarInt(g_SetHealth)
		// make sure that health will be set with old(1) or new method(2)
		SetEntProp(client, Prop_Data, "m_iMaxHealth", m_Health)
		SetEntityHealth(client, m_Health)
	}
	else if (Debug)
	{
		PrintToServer("[SYN] ERROR: Failed to set Health.")
	}
}

//------------//
// On Map End //
//------------//
public OnMapEnd()
{
	CheckMapEnd = true
	SetConVarInt(g_Items,0)
	SetConVarInt(g_OldRespawnSystem,0)
	SetConVarInt(g_FallDamage,0)
	SetConVarInt(g_Stuck,1)
	SetConVarInt(g_Explode,1)
	SetConVarInt(g_Kill,1)
	SetConVarInt(g_FakeBot,0)
	SetConVarInt(g_RemoveSave,0)
}

//-------------------------------------------------------------------------//
// Here we are returning the real player count without fake Player/Clients //
//-------------------------------------------------------------------------//
stock GetRealClientCount(bool:inGameOnly = true)
{
	new clients = 0
	for(new i = 1; i <= MaxClients; i++)
	{
		if(((inGameOnly) ? IsClientInGame(i) : IsClientConnected(i)) && !IsFakeClient(i))
		{
			clients++
		}
	}
	return clients
}

//-----------------------------------------------------------------------//
// Here we check if Player/Client is playing and returning true or false //
//-----------------------------------------------------------------------//
bool:PlayerSpawned(client)
{
	if (IsClientConnected(client) && IsClientInGame(client) && IsPlayerAlive(client) && !IsFakeClient(client))
	{
		return true
	}
	else
	{
		return false
	}
}

//-----------------------------------------------------------------------//
// Here we check if Player/Client is In Game and returning true or false //
//-----------------------------------------------------------------------//
bool:IsInGame(client)
{
	if (IsClientConnected(client) && IsClientInGame(client))
	{
		return true
	}
	else
	{
		return false
	}
}

//--------------------------------------------------------------------//
// Here we check if Player/Client is dead and returning true or false //
//--------------------------------------------------------------------//
bool:PlayerDead(client)
{
	if (IsClientConnected(client) && IsClientInGame(client) && !IsPlayerAlive(client) && !IsFakeClient(client))
	{
		return true
	}
	else
	{
		return false
	}
}
